package com.yugao.fintech.antelope.gateway.filter;

import com.yugao.fintech.antelope.gateway.config.MyGatewayProperties;
import com.yugao.fintech.antelope.base.model.constants.RequestCons;
import com.yugao.fintech.antelope.base.model.constants.SecurityCons;
import com.yugao.fintech.framework.assistant.utils.StringUtils;
import com.yugao.fintech.framework.assistant.utils.exception.BizException;
import com.yugao.fintech.framework.log.common.LogCons;
import com.yugao.fintech.framework.log.utils.MDCTraceUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 全局拦截器
 * 1. 对请求头中参数进行处理 from 参数进行清洗 2. 重写StripPrefix = 1,支持全局
 * 2. 支持swagger添加X-Forwarded-Prefix header （F SR2 已经支持，不需要自己维护）
 */
@Slf4j
@Component
public class LyRequestGlobalFilter implements GlobalFilter, Ordered {
    @Autowired
    private MyGatewayProperties properties;

    /**
     * Process the Web request and (optionally) delegate to the next {@code WebFilter}
     * through the given {@link GatewayFilterChain}.
     *
     * @param exchange the current server exchange
     * @param chain    provides a way to delegate to the next filter
     * @return {@code Mono<Void>} to indicate when request processing is complete
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getPath().toString();
        String loginId = exchange.getRequest().getHeaders().getFirst(LogCons.LOGIN_ID);
        // 排除websocket
        if ((StringUtils.isNotEmpty(loginId) && loginId.length() >= 16) && !path.contains("ws")) {
            throw new BizException("请求头中loginId值必须<=16");
        }

        ServerHttpRequest request = exchange.getRequest().mutate().headers(httpHeaders -> {
            // 清洗请求头中from 参数
            httpHeaders.remove(SecurityCons.FROM_SOURCE);

            // 设置请求时间
            httpHeaders.put(RequestCons.Headers.REQUEST_START_TIME,
                    Collections.singletonList(String.valueOf(System.currentTimeMillis())));

            // 设置traceId
            httpHeaders.put(LogCons.TRACE_ID, Collections.singletonList(MDCTraceUtils.getTraceId()));

            // 设置登录id, 前端会获取登录返回的loginId, 如果没有登录就随机生成一个
            httpHeaders.put(LogCons.LOGIN_ID, Collections.singletonList(loginId));

            Set<String> addRequestHeaders = properties.getSign().stream()
                    .flatMap(e -> e.getConfig().stream())
                    .filter(e -> CollectionUtils.isNotEmpty(e.getAddRequestHeader()))
                    .filter(e -> StringUtils.matches(path, new ArrayList<>(e.getIncludeUrls())))
                    .flatMap(e -> e.getAddRequestHeader().stream()).collect(Collectors.toSet());
            for (String addRequestHeader : addRequestHeaders) {
                String[] split = addRequestHeader.split("=");
                String key = split[0];
                String value = split[1];
                httpHeaders.put(key, Collections.singletonList(value));
            }

        }).build();

        // 2. 重写StripPrefix
//		addOriginalRequestUrl(exchange, request.getURI());
//		String rawPath = request.getURI().getRawPath();
//		String newPath = "/" + Arrays.stream(StringUtils.tokenizeToStringArray(rawPath, "/")).skip(1L)
//				.collect(Collectors.joining("/"));
//
//		ServerHttpRequest newRequest = request.mutate().path(newPath).build();
//		exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());

        return chain.filter(exchange.mutate().request(request.mutate().build()).build());
    }

    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE + 10;
    }

}
